package com.culturalCenter.dataCenter.globalconfig.filter;

import com.alibaba.fastjson.JSONArray;
import com.culturalCenter.dataCenter.Utils.*;
import com.culturalCenter.dataCenter.customEntity.CustomUser;
import com.culturalCenter.dataCenter.entity.JwtUser;
import com.culturalCenter.dataCenter.entity.user.Role;
import com.culturalCenter.dataCenter.entity.user.UserRole;
import com.culturalCenter.dataCenter.globalconfig.exception.ValidateCodeException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.encoder.org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;

/**
 * 登录时做处理
 *
 * @author Rockylee
 */
@Slf4j
public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    private String head;

    private String tokenHeader;

    private String userAsciiKey;

    private RedisTemplate<String, String> redisTemplate;

    public LoginAuthenticationFilter(AuthenticationManager authenticationManager, String head, String tokenHeader, String userAsciiKey, RedisTemplate<String, String> redisTemplate) {
        this.authenticationManager = authenticationManager;
        this.head = head;
        this.tokenHeader = tokenHeader;
        this.userAsciiKey = userAsciiKey;
        this.redisTemplate = redisTemplate;
        super.setFilterProcessesUrl("/api/login");
        // login
    }


    /**
     * 接收并解析用户登陆信息  /login,必须使用/login，和post方法才会进入此filter
     * 如果身份验证过程失败，就抛出一个AuthenticationException
     *
     * @param request
     * @param response
     *
     * @return
     *
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
            ObjectMapper mapper = new ObjectMapper();
            UsernamePasswordAuthenticationToken authRequest;
            Authentication authentication;
            try (InputStream stream = request.getInputStream()) {
                Map<String, String> body = mapper.readValue(stream, Map.class);
                log.info("用户(登录名)：{} 正在进行登录验证。。。密码：{}", body.get("username"), body.get("password"), body.get("code"));
                // if (body.containsKey("code") || body.get("code") == "" || body.get("code") == null) {
                //     throw new ValidateCodeException("验证码不存在", new Throwable("验证码不存在"));
                // } else {
                //
                //     // 这里进行验证码验证
                // }

                authRequest = new UsernamePasswordAuthenticationToken(
                        body.get("username"), body.get("password")
                );

                setDetails(request, authRequest);
                // 提交给自定义的provider组件进行身份验证和授权
                authentication = authenticationManager.authenticate(authRequest);
                return authentication;
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                LogException.LogError(log, e);
                authRequest = new UsernamePasswordAuthenticationToken("", "");
                authentication = authenticationManager.authenticate(authRequest);
                return authentication;
            } catch (AuthenticationException ex) {
                try {
                    LogException.LogError(log, ex);
                    response.setContentType("application/json;charset=UTF-8");
                    // PrintWriter out = response.getWriter();
                    response.setHeader(tokenHeader, head);
                    response.setStatus(200);
                    // ObjectMapper om = new ObjectMapper();
                    // out.write(om.writeValueAsString(ApiResult.instance("9999", ex.getMessage())));
                    // out.flush();
                    // out.close();
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                return super.attemptAuthentication(request, response);
            }
        } else {
            return super.attemptAuthentication(request, response);
        }
    }

    /**
     * 验证成功后,此方法会被调用,在此方法中生成token,并返回给客户端
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     *
     * @throws IOException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
        // 设置安全上下文。在当前的线程中，任何一处都可以通过SecurityContextHolder来获取当前用户认证成功的Authentication对象
        SecurityContextHolder.getContext().setAuthentication(authResult);
        log.debug("验证成功");
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter out = response.getWriter();
        JwtUser userDetails = (JwtUser) authResult.getPrincipal();
        if (authResult != null) {
            String account = userDetails.getUsername();
            Collection collection = userDetails.getAuthorities();
            // String authority = collection.iterator().next().toString();
            JSONArray.toJSONString(collection.toArray());

            // 生成token
            Map<String, Object> claims = new HashMap<>();
            claims.put("username", account);
            Map<String, List<String>> roleMap = new HashMap<>();
            List<String> strRole = new ArrayList<>();
            for (UserRole user : userDetails.getUserRoles()) {
                for (Role role : userDetails.getTbRoles()) {
                    if (user.getRoleId().equals(role.getId())) {
                        strRole.add(role.getRoleCode());
                    }
                }
            }
            roleMap.put("role", strRole);

            claims.put("role", roleMap);
            String token = JwtTokenUtils.generateToken(claims);
            // 过期时间
            Date time = JwtTokenUtils.getClaimsFromToken(token).getExpiration();

            // 保存token
            RedisUtil ru = new RedisUtil(redisTemplate);
            ru.setExpire(account, token, time.getTime());

            // 返回信息
            Map<String, Object> result = new HashMap<>();
            // result.put("authority", collection);
            // result.put("account", account);
            result.put("user", new CustomUser(userDetails));
            result.put("role", strRole);
            result.put("token", head + token);
            result.put("expire", time);

            response.setHeader(tokenHeader, head + token);
            response.setStatus(200);
            ObjectMapper om = new ObjectMapper();
            out.write(om.writeValueAsString(ApiResult.instance(ResultCode.SUCCESS, result)));
        }
        out.flush();
        out.close();
    }


    /**
     * 处理验证码认证失败
     */
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        SecurityContextHolder.clearContext();
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter out = response.getWriter();

        if (logger.isDebugEnabled()) {
            logger.debug("Authentication request failed: " + failed.toString(), failed);
            logger.debug("Updated SecurityContextHolder to contain null Authentication");
            logger.debug("Delegating to authentication failure handler " + failed);
        }
        ObjectMapper om = new ObjectMapper();
        if (failed.getMessage().equals("UserDetailsService returned null, which is an interface contract violation")) {
            out.write(om.writeValueAsString(ApiResult.instance(ResultCode.LOGIN_NAME_ERROR, null)));
        }

        if (failed.getMessage().equals("Bad credentials")) {
            out.write(om.writeValueAsString(ApiResult.instance(ResultCode.LOGIN_PASSWORD_ERROR, null)));
        }
        out.flush();
        out.close();
    }

    private void validate(String codeInRequest) {
        if (StringUtils.isBlank(codeInRequest)) {
            logger.info("验证码不能为空");
            throw new ValidateCodeException("验证码不能为空");
        }
        // if (codeInSession == null) {
        //     logger.info("验证码不存在");
        //     throw new ValidateCodeException("验证码不存在");
        // }
        // if (codeInSession.isExpried()) {
        //     logger.info("验证码已过期");
        //     throw new ValidateCodeException("验证码已过期");
        // }
        // if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
        //     logger.info("验证码不匹配" + "codeInSession:" + codeInSession.getCode() + ", codeInRequest:" + codeInRequest);
        //     throw new ValidateCodeException("验证码不匹配");
        // }
    }

}
